#include "bib_Hilo_Conexion.h"

unsigned __stdcall hiloConexion(char *nada);

extern CONFIG_EST config;
extern HANDLE heap;
extern DB *baseDatos;
extern struct REG_CACHE *finCache;
extern struct REG_CACHE *inicioCache;
extern HANDLE mutexDisco;
extern long posicionCabezal;
extern float tiempoLecturaSector;

int crearHiloConexion(HANDLE *hHilo){

	//Crear hilo
	if (crearHilo(NULL, &hiloConexion, hHilo))
		return EXIT_FAILURE;
	
	return EXIT_SUCCESS;
}
/*=====================================================================================*/
//Hilo del cliente
unsigned __stdcall hiloConexion(char *nada){
	struct HEADER_MPS solicitud;
	char *mensaje;
	SOCKET socketCli;		//Socket cliente

	//Establecer conexion
	if (inicializarWinsock())
		_endthreadex(0);
		
	if (abrirSocket(&socketCli)){
		finalizarWinsock();
		_endthreadex(0);
	}

	//Conectar al KSS
	if (conectar(socketCli)){
		finalizarWinsock();
		imprimirLog(ERROR,"No se pudo conectar con el KSS, funcional solo por consola","");	////Nro d error??
		_endthreadex(0);
	}

	//Handshake
	if (handshakeKSS(socketCli)){
		finalizarWinsock();
		cerrarSocket(socketCli);
		_endthreadex(0);
	}

	if (alocarMemoria(heap, (TAM_SECTOR + 14) * 2 * sizeof(char), &mensaje))
		return EXIT_FAILURE;

	//Responder solicitudes del KSS
	while(1){
		if (recibirMensajeMPS(socketCli, &solicitud, mensaje)){
			imprimirLog(ERROR,"Fallo en la solicitud MPS, funcional solo por consola","");
			finalizarWinsock();
			cerrarSocket(socketCli);
			liberarMemoria(heap, mensaje);
			_endthreadex(0);
		}

		//Ejecutar comando
		if (solicitud.payloadLength <= 0){
			imprimirLog(WARNING, "Solicitud MPS con longitud de PayLoad invalido", "");	////Convertir longitud a char
		}
		
		if (tomarControlMutex(mutexDisco))
			return EXIT_FAILURE;
		
		if (solicitud.payloadDescriptor == API_GET_SECTORES){
			if(apiGetSectores(mensaje, socketCli, solicitud.descriptorID)){
				cerrarSocket(socketCli);
				finalizarWinsock();
				liberarMemoria(heap, mensaje);
				_endthreadex(0);
			}
		}

		else if (solicitud.payloadDescriptor == API_PUT_SECTORES){
			if(apiPutSectores(mensaje, socketCli, solicitud.descriptorID)){
				cerrarSocket(socketCli);
				finalizarWinsock();
				liberarMemoria(heap, mensaje);
				_endthreadex(0);
			}
		}

		else if (solicitud.payloadDescriptor == API_GET_CHS){
			if(apiGetCHS(socketCli, solicitud.descriptorID)){
				cerrarSocket(socketCli);
				finalizarWinsock();
				liberarMemoria(heap, mensaje);
				_endthreadex(0);
			}
		}
		
		if (liberarControlMutex(mutexDisco))
			return EXIT_FAILURE;
	}

	_endthreadex(0);
}
/*=====================================================================================*/
//Handshake
int handshakeKSS(SOCKET socketCli){
	struct HEADER_MPS mensaje;
	
	generarDescriptorID(mensaje.descriptorID);
	mensaje.payloadDescriptor = TIPO_PROC_VDA;
	mensaje.payloadLength = (long) strlen(config.nombre) + sizeof(char);

	//Request
	if (enviarMensajeMPS(socketCli, &mensaje, config.nombre)){
		imprimirLog(WARNING, "Fallo en el Handshake, funcional solo por consola", "Request");
		return EXIT_FAILURE;
	}

	//Response
	if (recibirMensajeMPS(socketCli, &mensaje, NULL)){
		imprimirLog(WARNING, "Fallo en el Handshake, funcional solo por consola", "Response");
		return EXIT_FAILURE;
	}

	//Comprobar Respuesta
	if (mensaje.payloadDescriptor == RESPUESTA_FAIL){
		//El KSS rechazo la conexion
		imprimirLog(WARNING, "El KSS rechazo la solicitud de conexion, funcional solo por consola", "");
		return EXIT_FAILURE;
	}

	//Conexion aceptada
	return EXIT_SUCCESS;
}
/*=====================================================================================*/
int apiGetSectores(char *mensaje, SOCKET sock, char *descriptorID){

	char *respuesta;
	struct HEADER_MPS cabezera;
	long sectores[2];
	int cantSectores;
	
	mensaje = mensaje + strlen("getSectores(");
	
	memcpy(&sectores[0], mensaje, sizeof(long));
	memcpy(&sectores[1], mensaje + sizeof(long), sizeof(long));
	
	if (sectores[1] == -1)
		cantSectores = 1;
	else
		cantSectores = 2;

	if (obtenerSectores(sectores, cantSectores, FLAG_CONEXION))
		return EXIT_FAILURE;
		
	if (alocarMemoria(heap, TAM_SECTOR * 2 * sizeof(char), &respuesta))
		return EXIT_FAILURE;
		
	memset(respuesta, '\0', TAM_SECTOR * 2);
	
	if (cantSectores == 1){
		strcpy(respuesta, finCache->contenido);
	}
	else{
		anteUltimaPosicion(respuesta);
		strcpy(respuesta + TAM_SECTOR, finCache->contenido);
	}
	
	strcpy(cabezera.descriptorID, descriptorID);
	cabezera.payloadDescriptor = RESPUESTA_OK;
	cabezera.payloadLength = TAM_SECTOR * 2;

	if (enviarMensajeMPS(sock, &cabezera, respuesta)){
		imprimirLog(WARNING, "No se pudo responder al GetSectores()", "");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}
/*=====================================================================================*/
int apiPutSectores(char *mensaje, SOCKET sock, char *descriptorID){

	long sector;
	char *msg;
	int dirCilindro, dirSector;
	int cilindroActual, sectorActual;
	int sentido;
	int i;
	int bytesEscritos = 0;
	struct HEADER_MPS cabezera;
			
	deLogicaAFisica(posicionCabezal, &cilindroActual, &sectorActual);

	sprintf(mensaje, "%d", posicionCabezal);
	imprimirLog(INFO, "Posicion actual:", mensaje);
	
	mensaje = mensaje + strlen("putSectores(");
	
	memcpy(&sector, mensaje, sizeof(long));
	
	for (i = 0; i < 2 && sector != -1; i++){
	
		long cantMovimientos = 0;
	
		if (alocarMemoria(heap, (DIGITOS_MAX_DIR_LOGICA + 2) * (config.sectores + config.cilindros) + 1, &msg))
			break;
	
		strcpy(msg, "");

		deLogicaAFisica(sector, &dirCilindro, &dirSector);
	
		if (dirCilindro < cilindroActual)
			sentido = (-1);
		else
			sentido = 1;
	
		while (dirCilindro != cilindroActual){
			cilindroActual += sentido;
			sectorActual = (sectorActual + 1) % config.sectores;
			cantMovimientos++;
			deFisicaALogica(&posicionCabezal, cilindroActual, sectorActual);
			sprintf(msg, "%s, %d", msg, posicionCabezal);
		}
	
		while (dirSector != sectorActual){
			sectorActual = (sectorActual + 1) % config.sectores;
			cantMovimientos++;
			deFisicaALogica(&posicionCabezal, cilindroActual, sectorActual);
			sprintf(msg, "%s, %d", msg, posicionCabezal);
		}
	
		if (!cantMovimientos){
			sprintf(msg, "%d", posicionCabezal);
			imprimirLog(INFO, "Sectores atravesados", msg);
		}
		else
			imprimirLog(INFO, "Sectores atravesados", msg + 2);
			
		sprintf(msg, "%.2f ms", cantMovimientos * tiempoLecturaSector);
		imprimirLog(INFO, "Tiempo Consumido", msg);
	
		mensaje = mensaje + sizeof(long);
	
		if (escribirBD(baseDatos, &sector, mensaje))
			break;
			
		bytesEscritos = bytesEscritos + TAM_SECTOR;

		mensaje = mensaje + TAM_SECTOR;
		memcpy(&sector, mensaje, sizeof(long));
		liberarMemoria(heap, msg);
	}
	
	strcpy(cabezera.descriptorID, descriptorID);
	cabezera.payloadDescriptor = RESPUESTA_OK;
	cabezera.payloadLength = sizeof(int);

	if (enviarMensajeMPS(sock, &cabezera, (char *) &bytesEscritos)){
		imprimirLog(WARNING, "No se pudo responder al PutSectores()", "");
		return EXIT_FAILURE;
	}
	
	return EXIT_SUCCESS;
}
/*=====================================================================================*/
int apiGetCHS(SOCKET sock, char *descriptorID){

	char respuesta[3 * sizeof(int)];
	struct HEADER_MPS cabezera;

	strcpy(cabezera.descriptorID, descriptorID);
	cabezera.payloadDescriptor = RESPUESTA_OK;
	cabezera.payloadLength = 3 * sizeof(int);

	memcpy(respuesta, &(config.cilindros), sizeof(int));
	memcpy(respuesta + sizeof(int), &(config.cabezales), sizeof(int));
	memcpy(respuesta + 2 * sizeof(int), &(config.sectores), sizeof(int));

	if (enviarMensajeMPS(sock, &cabezera, respuesta)){
		imprimirLog(WARNING, "No se pudo responder al GetCHS()", "");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}
/*=====================================================================================*/
int actualizarCache(long dirLogica, char *datosSector){

	struct REG_CACHE *anterior = NULL;
	struct REG_CACHE *ptr = inicioCache;

	while(ptr){
		if (ptr->sector != dirLogica){
			anterior = ptr;
			ptr = ptr->siguiente;
		}
		else
			break;
	}
	
	if(ptr){
		strcpy(ptr->contenido, datosSector);
		ponerAlFinal(anterior, ptr);	
	}
	else{
		if (agregarRegCache(dirLogica, datosSector, heap))
			return EXIT_FAILURE;
			
		if (eliminarRegCache(heap))
			return EXIT_FAILURE;	
	}

	return EXIT_SUCCESS;
}